home *** CD-ROM | disk | FTP | other *** search
/ PC/CD Gamer UK 120 / CD Gamer Issue 120 (March 2003) (Disc 2).ISO / mods / Q2_Codered / codeRED1_0.exe / Data1.cab / g_ai.c1 < prev    next >
Encoding:
Text File  |  2002-12-07  |  25.8 KB  |  1,130 lines

  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20. // g_ai.c
  21.  
  22. #include "g_local.h"
  23.  
  24. qboolean FindTarget (edict_t *self);
  25. extern cvar_t    *maxclients;
  26.  
  27. qboolean ai_checkattack (edict_t *self, float dist);
  28.  
  29. qboolean    enemy_vis;
  30. qboolean    enemy_infront;
  31. int            enemy_range;
  32. float        enemy_yaw;
  33.  
  34. //============================================================================
  35.  
  36.  
  37. /*
  38. =================
  39. AI_SetSightClient
  40.  
  41. Called once each frame to set level.sight_client to the
  42. player to be checked for in findtarget.
  43.  
  44. If all clients are either dead or in notarget, sight_client
  45. will be null.
  46.  
  47. In coop games, sight_client will cycle between the clients.
  48. =================
  49. */
  50. void AI_SetSightClient (void)
  51. {
  52.     edict_t    *ent;
  53.     int        start, check;
  54.  
  55.     if (level.sight_client == NULL)
  56.         start = 1;
  57.     else
  58.         start = level.sight_client - g_edicts;
  59.  
  60.     check = start;
  61.     while (1)
  62.     {
  63.         check++;
  64.         if (check > game.maxclients)
  65.             check = 1;
  66.         ent = &g_edicts[check];
  67.         if (ent->inuse
  68.             && ent->health > 0
  69.             && !(ent->flags & FL_NOTARGET) )
  70.         {
  71.             level.sight_client = ent;
  72.             return;        // got one
  73.         }
  74.         if (check == start)
  75.         {
  76.             level.sight_client = NULL;
  77.             return;        // nobody to see
  78.         }
  79.     }
  80. }
  81.  
  82. //============================================================================
  83.  
  84. /*
  85. =============
  86. ai_move
  87.  
  88. Move the specified distance at current facing.
  89. This replaces the QC functions: ai_forward, ai_back, ai_pain, and ai_painforward
  90. ==============
  91. */
  92. void ai_move (edict_t *self, float dist)
  93. {
  94.     M_walkmove (self, self->s.angles[YAW], dist);
  95. }
  96.  
  97.  
  98. /*
  99. =============
  100. ai_stand
  101.  
  102. Used for standing around and looking for players
  103. Distance is for slight position adjustments needed by the animations
  104. ==============
  105. */
  106. void ai_stand (edict_t *self, float dist)
  107. {
  108.     vec3_t    v;
  109.  
  110.     if (dist)
  111.         M_walkmove (self, self->s.angles[YAW], dist);
  112.  
  113.     if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  114.     {
  115.         if (self->enemy)
  116.         {
  117.             VectorSubtract (self->enemy->s.origin, self->s.origin, v);
  118.             self->ideal_yaw = vectoyaw(v);
  119.             if (self->s.angles[YAW] != self->ideal_yaw && self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
  120.             {
  121.                 self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
  122.                 self->monsterinfo.run (self);
  123.             }
  124.             M_ChangeYaw (self);
  125.             ai_checkattack (self, 0);
  126.         }
  127.         else
  128.             FindTarget (self);
  129.         return;
  130.     }
  131.  
  132.     if (FindTarget (self))
  133.         return;
  134.     
  135.     if (level.time > self->monsterinfo.pausetime)
  136.     {
  137.         self->monsterinfo.walk (self);
  138.         return;
  139.     }
  140.  
  141.     if (!(self->spawnflags & 1) && (self->monsterinfo.idle) && (level.time > self->monsterinfo.idle_time))
  142.     {
  143.         if (self->monsterinfo.idle_time)
  144.         {
  145.             self->monsterinfo.idle (self);
  146.             self->monsterinfo.idle_time = level.time + 15 + random() * 15;
  147.         }
  148.         else
  149.         {
  150.             self->monsterinfo.idle_time = level.time + random() * 15;
  151.         }
  152.     }
  153. }
  154.  
  155.  
  156. /*
  157. =============
  158. ai_walk
  159.  
  160. The monster is walking it's beat
  161. =============
  162. */
  163. void ai_walk (edict_t *self, float dist)
  164. {
  165.     M_MoveToGoal (self, dist);
  166.  
  167.     // check for noticing a player
  168.     if (FindTarget (self))
  169.         return;
  170.  
  171.     if ((self->monsterinfo.search) && (level.time > self->monsterinfo.idle_time))
  172.     {
  173.         if (self->monsterinfo.idle_time)
  174.         {
  175.             self->monsterinfo.search (self);
  176.             self->monsterinfo.idle_time = level.time + 15 + random() * 15;
  177.         }
  178.         else
  179.         {
  180.             self->monsterinfo.idle_time = level.time + random() * 15;
  181.         }
  182.     }
  183. }
  184.  
  185.  
  186. /*
  187. =============
  188. ai_charge
  189.  
  190. Turns towards target and advances
  191. Use this call with a distnace of 0 to replace ai_face
  192. ==============
  193. */
  194. void ai_charge (edict_t *self, float dist)
  195. {
  196.     vec3_t    v;
  197.  
  198.     VectorSubtract (self->enemy->s.origin, self->s.origin, v);
  199.     self->ideal_yaw = vectoyaw(v);
  200.     M_ChangeYaw (self);
  201.  
  202.     if (dist)
  203.         M_walkmove (self, self->s.angles[YAW], dist);
  204. }
  205.  
  206.  
  207. /*
  208. =============
  209. ai_turn
  210.  
  211. don't move, but turn towards ideal_yaw
  212. Distance is for slight position adjustments needed by the animations
  213. =============
  214. */
  215. void ai_turn (edict_t *self, float dist)
  216. {
  217.     if (dist)
  218.         M_walkmove (self, self->s.angles[YAW], dist);
  219.  
  220.     if (FindTarget (self))
  221.         return;
  222.     
  223.     M_ChangeYaw (self);
  224. }
  225.  
  226. void ai_still (edict_t *self, float dist)
  227. {
  228.     if (dist)
  229.         M_walkmove (self, self->s.angles[YAW], dist);
  230.  
  231.     M_ChangeYaw (self);
  232. }
  233. /*
  234.  
  235. .enemy
  236. Will be world if not currently angry at anyone.
  237.  
  238. .movetarget
  239. The next path spot to walk toward.  If .enemy, ignore .movetarget.
  240. When an enemy is killed, the monster will try to return to it's path.
  241.  
  242. .hunt_time
  243. Set to time + something when the player is in sight, but movement straight for
  244. him is blocked.  This causes the monster to use wall following code for
  245. movement direction instead of sighting on the player.
  246.  
  247. .ideal_yaw
  248. A yaw angle of the intended direction, which will be turned towards at up
  249. to 45 deg / state.  If the enemy is in view and hunt_time is not active,
  250. this will be the exact line towards the enemy.
  251.  
  252. .pausetime
  253. A monster will leave it's stand state and head towards it's .movetarget when
  254. time > .pausetime.
  255.  
  256. walkmove(angle, speed) primitive is all or nothing
  257. */
  258.  
  259. /*
  260. =============
  261. range
  262.  
  263. returns the range catagorization of an entity reletive to self
  264. 0    melee range, will become hostile even if back is turned
  265. 1    visibility and infront, or visibility and show hostile
  266. 2    infront and show hostile
  267. 3    only triggered by damage
  268. =============
  269. */
  270. int range (edict_t *self, edict_t *other)
  271. {
  272.     vec3_t    v;
  273.     float    len;
  274.  
  275.     VectorSubtract (self->s.origin, other->s.origin, v);
  276.     len = VectorLength (v);
  277.     if (len < MELEE_DISTANCE)
  278.         return RANGE_MELEE;
  279.     if (len < 500)
  280.         return RANGE_NEAR;
  281.     if (len < 1000)
  282.         return RANGE_MID;
  283.     return RANGE_FAR;
  284. }
  285.  
  286. /*
  287. =============
  288. visible
  289.  
  290. returns 1 if the entity is visible to self, even if not infront ()
  291. =============
  292. */
  293. qboolean visible (edict_t *self, edict_t *other)
  294. {
  295.     vec3_t    spot1;
  296.     vec3_t    spot2;
  297.     trace_t    trace;
  298.  
  299.     VectorCopy (self->s.origin, spot1);
  300.     spot1[2] += self->viewheight;
  301.     VectorCopy (other->s.origin, spot2);
  302.     spot2[2] += other->viewheight;
  303.     trace = gi.trace (spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE);
  304.     
  305.     if (trace.fraction == 1.0)
  306.         return true;
  307.     return false;
  308. }
  309.  
  310.  
  311. /*
  312. =============
  313. infront
  314.  
  315. returns 1 if the entity is in front (in sight) of self
  316. =============
  317. */
  318. qboolean infront (edict_t *self, edict_t *other)
  319. {
  320.     vec3_t    vec;
  321.     float    dot;
  322.     vec3_t    forward;
  323.     
  324.     AngleVectors (self->s.angles, forward, NULL, NULL);
  325.     VectorSubtract (other->s.origin, self->s.origin, vec);
  326.     VectorNormalize (vec);
  327.     dot = DotProduct (vec, forward);
  328.     
  329.     if (dot > 0.3)
  330.         return true;
  331.     return false;
  332. }
  333.  
  334.  
  335. //============================================================================
  336.  
  337. void HuntTarget (edict_t *self)
  338. {
  339.     vec3_t    vec;
  340.  
  341.     if (self->monsterinfo.aiflags & AI_DUCKED)
  342.         return;
  343.  
  344.     self->goalentity = self->enemy;
  345.     if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  346.         self->monsterinfo.stand (self);
  347.     else
  348.         self->monsterinfo.run (self);
  349.     VectorSubtract (self->enemy->s.origin, self->s.origin, vec);
  350.     self->ideal_yaw = vectoyaw(vec);
  351.     // wait a while before first attack
  352.     if (!(self->monsterinfo.aiflags & AI_STAND_GROUND))
  353.         AttackFinished (self, 1);
  354. }
  355.  
  356. void FoundTarget (edict_t *self)
  357. {
  358.     if (self->monsterinfo.aiflags & AI_DUCKED)
  359.         return;
  360.  
  361.     // let other monsters see this monster for a while
  362.     if (self->enemy->client)
  363.     {
  364.         level.sight_entity = self;
  365.         level.sight_entity_framenum = level.framenum;
  366.         level.sight_entity->light_level = 128;
  367.     }
  368.  
  369.     self->show_hostile = level.time + 1;        // wake up other monsters
  370.  
  371.     VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting);
  372.     self->monsterinfo.trail_time = level.time;
  373.  
  374.     if (!self->combattarget)
  375.     {
  376.         HuntTarget (self);
  377.         return;
  378.     }
  379.  
  380.     self->goalentity = self->movetarget = G_PickTarget(self->combattarget);
  381.     if (!self->movetarget)
  382.     {
  383.         self->goalentity = self->movetarget = self->enemy;
  384.         HuntTarget (self);
  385.         gi.dprintf("%s at %s, combattarget %s not found\n", self->classname, vtos(self->s.origin), self->combattarget);
  386.         return;
  387.     }
  388.  
  389.     // clear out our combattarget, these are a one shot deal
  390.     self->combattarget = NULL;
  391.     self->monsterinfo.aiflags |= AI_COMBAT_POINT;
  392.  
  393.     // clear the targetname, that point is ours!
  394.     self->movetarget->targetname = NULL;
  395.     self->monsterinfo.pausetime = 0;
  396.  
  397.     // run for it
  398.     self->monsterinfo.run (self);
  399. }
  400.  
  401.  
  402. /*
  403. ===========
  404. FindTarget
  405.  
  406. Self is currently not attacking anything, so try to find a target
  407.  
  408. Returns TRUE if an enemy was sighted
  409.  
  410. When a player fires a missile, the point of impact becomes a fakeplayer so
  411. that monsters that see the impact will respond as if they had seen the
  412. player.
  413.  
  414. To avoid spending too much time, only a single client (or fakeclient) is
  415. checked each frame.  This means multi player games will have slightly
  416. slower noticing monsters.
  417. ============
  418. */
  419. qboolean FindTarget (edict_t *self)
  420. {
  421.     edict_t        *client;
  422.     qboolean    heardit;
  423.     int            r;
  424.  
  425.     if (self->monsterinfo.aiflags & AI_GOOD_GUY)
  426.     {
  427.         if (self->goalentity && self->goalentity->inuse && self->goalentity->classname)
  428.         {
  429.             if (strcmp(self->goalentity->classname, "target_actor") == 0)
  430.                 return false;
  431.         }
  432.  
  433.         //FIXME look for monsters?
  434.         return false;
  435.     }
  436.  
  437.     // if we're going to a combat point, just proceed
  438.     if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
  439.         return false;
  440.  
  441. // if the first spawnflag bit is set, the monster will only wake up on
  442. // really seeing the player, not another monster getting angry or hearing
  443. // something
  444.  
  445. // revised behavior so they will wake up if they "see" a player make a noise
  446. // but not weapon impact/explosion noises
  447.  
  448.     heardit = false;
  449.     if ((level.sight_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) )
  450.     {
  451.         client = level.sight_entity;
  452.         if (client->enemy == self->enemy)
  453.         {
  454.             return false;
  455.         }
  456.     }
  457.     else if (level.sound_entity_framenum >= (level.framenum - 1))
  458.     {
  459.         client = level.sound_entity;
  460.         heardit = true;
  461.     }
  462.     else if (!(self->enemy) && (level.sound2_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) )
  463.     {
  464.         client = level.sound2_entity;
  465.         heardit = true;
  466.     }
  467.     else
  468.     {
  469.         client = level.sight_client;
  470.         if (!client)
  471.             return false;    // no clients to get mad at
  472.     }
  473.  
  474.     // if the entity went away, forget it
  475.     if (!client->inuse)
  476.         return false;
  477.  
  478.     if (client == self->enemy)
  479.         return true;    // JDC false;
  480.  
  481.     if (client->client)
  482.     {
  483.         if (client->flags & FL_NOTARGET)
  484.             return false;
  485.     }
  486.     else if (client->svflags & SVF_MONSTER)
  487.     {
  488.         if (!client->enemy)
  489.             return false;
  490.         if (client->enemy->flags & FL_NOTARGET)
  491.             return false;
  492.     }
  493.     else if (heardit)
  494.     {
  495.         if (client->owner->flags & FL_NOTARGET)
  496.             return false;
  497.     }
  498.     else
  499.         return false;
  500.  
  501.     if (!heardit)
  502.     {
  503.         r = range (self, client);
  504.  
  505.         if (r == RANGE_FAR)
  506.             return false;
  507.  
  508. // this is where we would check invisibility
  509.  
  510.         // is client in an spot too dark to be seen?
  511.         if (client->light_level <= 5)
  512.             return false;
  513.  
  514.         if (!visible (self, client))
  515.         {
  516.             return false;
  517.         }
  518.  
  519.         if (r == RANGE_NEAR)
  520.         {
  521.             if (client->show_hostile < level.time && !infront (self, client))
  522.             {
  523.                 return false;
  524.             }
  525.         }
  526.         else if (r == RANGE_MID)
  527.         {
  528.             if (!infront (self, client))
  529.             {
  530.                 return false;
  531.             }
  532.         }
  533.  
  534.         self->enemy = client;
  535.  
  536.         if (strcmp(self->enemy->classname, "player_noise") != 0)
  537.         {
  538.             self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
  539.  
  540.             if (!self->enemy->client)
  541.             {
  542.                 self->enemy = self->enemy->enemy;
  543.                 if (!self->enemy->client)
  544.                 {
  545.                     self->enemy = NULL;
  546.                     return false;
  547.                 }
  548.             }
  549.         }
  550.     }
  551.     else    // heardit
  552.     {
  553.         vec3_t    temp;
  554.  
  555.         if (self->spawnflags & 1)
  556.         {
  557.             if (!visible (self, client))
  558.                 return false;
  559.         }
  560.         else
  561.         {
  562.             if (!gi.inPHS(self->s.origin, client->s.origin))
  563.                 return false;
  564.         }
  565.  
  566.         VectorSubtract (client->s.origin, self->s.origin, temp);
  567.  
  568.         if (VectorLength(temp) > 1000)    // too far to hear
  569.         {
  570.             return false;
  571.         }
  572.  
  573.         // check area portals - if they are different and not connected then we can't hear it
  574.         if (client->areanum != self->areanum)
  575.             if (!gi.AreasConnected(self->areanum, client->areanum))
  576.                 return false;
  577.  
  578.         self->ideal_yaw = vectoyaw(temp);
  579.         M_ChangeYaw (self);
  580.  
  581.         // hunt the sound for a bit; hopefully find the real player
  582.         self->monsterinfo.aiflags |= AI_SOUND_TARGET;
  583.         self->enemy = client;
  584.     }
  585.  
  586. //
  587. // got one
  588. //
  589.     FoundTarget (self);
  590.  
  591.     if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight))
  592.         self->monsterinfo.sight (self, self->enemy);
  593.  
  594.     return true;
  595. }
  596.  
  597.  
  598. //=============================================================================
  599.  
  600. /*
  601. ============
  602. FacingIdeal
  603.  
  604. ============
  605. */
  606. qboolean FacingIdeal(edict_t *self)
  607. {
  608.     float    delta;
  609.  
  610.     delta = anglemod(self->s.angles[YAW] - self->ideal_yaw);
  611.     if (delta > 45 && delta < 315)
  612.         return false;
  613.     return true;
  614. }
  615.  
  616.  
  617. //=============================================================================
  618.  
  619. qboolean M_CheckAttack (edict_t *self)
  620. {
  621.     vec3_t    spot1, spot2;
  622.     float    chance;
  623.     trace_t    tr;
  624.  
  625.     if (self->enemy->health > 0)
  626.     {
  627.     // see if any entities are in the way of the shot
  628.         VectorCopy (self->s.origin, spot1);
  629.         spot1[2] += self->viewheight;
  630.         VectorCopy (self->enemy->s.origin, spot2);
  631.         spot2[2] += self->enemy->viewheight;
  632.  
  633.         tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WINDOW);
  634.  
  635.         // do we have a clear shot?
  636.         if (tr.ent != self->enemy)
  637.             return false;
  638.     }
  639.     
  640.     // melee attack
  641.     if (enemy_range == RANGE_MELEE)
  642.     {
  643.         // don't always melee in easy mode
  644.         if (skill->value == 0 && (rand()&3) )
  645.             return false;
  646.         if (self->monsterinfo.melee)
  647.             self->monsterinfo.attack_state = AS_MELEE;
  648.         else
  649.             self->monsterinfo.attack_state = AS_MISSILE;
  650.         return true;
  651.     }
  652.     
  653. // missile attack
  654.     if (!self->monsterinfo.attack)
  655.         return false;
  656.         
  657.     if (level.time < self->monsterinfo.attack_finished)
  658.         return false;
  659.         
  660.     if (enemy_range == RANGE_FAR)
  661.         return false;
  662.  
  663.     if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  664.     {
  665.         chance = 0.4;
  666.     }
  667.     else if (enemy_range == RANGE_MELEE)
  668.     {
  669.         chance = 0.2;
  670.     }
  671.     else if (enemy_range == RANGE_NEAR)
  672.     {
  673.         chance = 0.1;
  674.     }
  675.     else if (enemy_range == RANGE_MID)
  676.     {
  677.         chance = 0.02;
  678.     }
  679.     else
  680.     {
  681.         return false;
  682.     }
  683.  
  684.     if (skill->value == 0)
  685.         chance *= 0.5;
  686.     else if (skill->value >= 2)
  687.         chance *= 2;
  688.  
  689.     if (random () < chance)
  690.     {
  691.         self->monsterinfo.attack_state = AS_MISSILE;
  692.         self->monsterinfo.attack_finished = level.time + 2*random();
  693.         return true;
  694.     }
  695.  
  696.     if (self->flags & FL_FLY)
  697.     {
  698.         if (random() < 0.3)
  699.             self->monsterinfo.attack_state = AS_SLIDING;
  700.         else
  701.             self->monsterinfo.attack_state = AS_STRAIGHT;
  702.     }
  703.  
  704.     return false;
  705. }
  706.  
  707.  
  708. /*
  709. =============
  710. ai_run_melee
  711.  
  712. Turn and close until within an angle to launch a melee attack
  713. =============
  714. */
  715. void ai_run_melee(edict_t *self)
  716. {
  717.     self->ideal_yaw = enemy_yaw;
  718.     M_ChangeYaw (self);
  719.  
  720.     if (FacingIdeal(self))
  721.     {
  722.         self->monsterinfo.melee (self);
  723.         self->monsterinfo.attack_state = AS_STRAIGHT;
  724.     }
  725. }
  726.  
  727.  
  728. /*
  729. =============
  730. ai_run_missile
  731.  
  732. Turn in place until within an angle to launch a missile attack
  733. =============
  734. */
  735. void ai_run_missile (edict_t *self)
  736. {
  737.     self->ideal_yaw = enemy_yaw;
  738.     M_ChangeYaw (self);
  739.  
  740.     if (FacingIdeal(self))
  741.     {
  742.         self->monsterinfo.attack (self);
  743.         self->monsterinfo.attack_state = AS_STRAIGHT;
  744.     }
  745. };
  746.  
  747.  
  748. /*
  749. =============
  750. ai_run_slide
  751.  
  752. Strafe sideways, but stay at aproximately the same range
  753. =============
  754. */
  755. void ai_run_slide(edict_t *self, float distance)
  756. {
  757.     float    ofs;
  758.     
  759.     self->ideal_yaw = enemy_yaw;
  760.     M_ChangeYaw (self);
  761.  
  762.     if (self->monsterinfo.lefty)
  763.         ofs = 90;
  764.     else
  765.         ofs = -90;
  766.     
  767.     if (M_walkmove (self, self->ideal_yaw + ofs, distance))
  768.         return;
  769.         
  770.     self->monsterinfo.lefty = 1 - self->monsterinfo.lefty;
  771.     M_walkmove (self, self->ideal_yaw - ofs, distance);
  772. }
  773.  
  774.  
  775. /*
  776. =============
  777. ai_checkattack
  778.  
  779. Decides if we're going to attack or do something else
  780. used by ai_run and ai_stand
  781. =============
  782. */
  783. qboolean ai_checkattack (edict_t *self, float dist)
  784. {
  785.     vec3_t        temp;
  786.     qboolean    hesDeadJim;
  787.  
  788. // this causes monsters to run blindly to the combat point w/o firing
  789.     if (self->goalentity)
  790.     {
  791.         if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
  792.             return false;
  793.  
  794.         if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
  795.         {
  796.             if ((level.time - self->enemy->teleport_time) > 5.0)
  797.             {
  798.                 if (self->goalentity == self->enemy)
  799.                     if (self->movetarget)
  800.                         self->goalentity = self->movetarget;
  801.                     else
  802.                         self->goalentity = NULL;
  803.                 self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
  804.                 if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
  805.                     self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
  806.             }
  807.             else
  808.             {
  809.                 self->show_hostile = level.time + 1;
  810.                 return false;
  811.             }
  812.         }
  813.     }
  814.  
  815.     enemy_vis = false;
  816.  
  817. // see if the enemy is dead
  818.     hesDeadJim = false;
  819.     if ((!self->enemy) || (!self->enemy->inuse))
  820.     {
  821.         hesDeadJim = true;
  822.     }
  823.     else if (self->monsterinfo.aiflags & AI_MEDIC)
  824.     {
  825.         if (self->enemy->health > 0)
  826.         {
  827.             hesDeadJim = true;
  828.             self->monsterinfo.aiflags &= ~AI_MEDIC;
  829.         }
  830.     }
  831.     else
  832.     {
  833.         if (self->monsterinfo.aiflags & AI_BRUTAL)
  834.         {
  835.             if (self->enemy->health <= -self->enemy->gib_health)
  836.                 hesDeadJim = true;
  837.         }
  838.         else
  839.         {
  840.             if (self->enemy->health <= 0)
  841.                 hesDeadJim = true;
  842.         }
  843.     }
  844.  
  845.     if (hesDeadJim)
  846.     {
  847.         self->enemy = NULL;
  848.     // FIXME: look all around for other targets
  849.         if (self->oldenemy && self->oldenemy->health > 0)
  850.         {
  851.             self->enemy = self->oldenemy;
  852.             self->oldenemy = NULL;
  853.             HuntTarget (self);
  854.         }
  855.         else
  856.         {
  857.             if (self->movetarget)
  858.             {
  859.                 self->goalentity = self->movetarget;
  860.                 self->monsterinfo.walk (self);
  861.             }
  862.             else
  863.             {
  864.                 // we need the pausetime otherwise the stand code
  865.                 // will just revert to walking with no target and
  866.                 // the monsters will wonder around aimlessly trying
  867.                 // to hunt the world entity
  868.                 self->monsterinfo.pausetime = level.time + 100000000;
  869.                 self->monsterinfo.stand (self);
  870.             }
  871.             return true;
  872.         }
  873.     }
  874.  
  875.     self->show_hostile = level.time + 1;        // wake up other monsters
  876.  
  877. // check knowledge of enemy
  878.     enemy_vis = visible(self, self->enemy);
  879.     if (enemy_vis)
  880.     {
  881.         self->monsterinfo.search_time = level.time + 5;
  882.         VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
  883.     }
  884.  
  885. // look for other coop players here
  886. //    if (coop && self->monsterinfo.search_time < level.time)
  887. //    {
  888. //        if (FindTarget (self))
  889. //            return true;
  890. //    }
  891.  
  892.     enemy_infront = infront(self, self->enemy);
  893.     enemy_range = range(self, self->enemy);
  894.     VectorSubtract (self->enemy->s.origin, self->s.origin, temp);
  895.     enemy_yaw = vectoyaw(temp);
  896.  
  897.  
  898.     // JDC self->ideal_yaw = enemy_yaw;
  899.  
  900.     if (self->monsterinfo.attack_state == AS_MISSILE)
  901.     {
  902.         ai_run_missile (self);
  903.         return true;
  904.     }
  905.     if (self->monsterinfo.attack_state == AS_MELEE)
  906.     {
  907.         ai_run_melee (self);
  908.         return true;
  909.     }
  910.  
  911.     // if enemy is not currently visible, we will never attack
  912.     if (!enemy_vis)
  913.         return false;
  914.  
  915.     return self->monsterinfo.checkattack (self);
  916. }
  917.  
  918.  
  919. /*
  920. =============
  921. ai_run
  922.  
  923. The monster has an enemy it is trying to kill
  924. =============
  925. */
  926. void ai_run (edict_t *self, float dist)
  927. {
  928.     vec3_t        v;
  929.     edict_t        *tempgoal;
  930.     edict_t        *save;
  931.     qboolean    new;
  932.     edict_t        *marker;
  933.     float        d1, d2;
  934.     trace_t        tr;
  935.     vec3_t        v_forward, v_right;
  936.     float        left, center, right;
  937.     vec3_t        left_target, right_target;
  938.  
  939.     // if we're going to a combat point, just proceed
  940.     if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
  941.     {
  942.         M_MoveToGoal (self, dist);
  943.         return;
  944.     }
  945.  
  946.     if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
  947.     {
  948.         VectorSubtract (self->s.origin, self->enemy->s.origin, v);
  949.         if (VectorLength(v) < 64)
  950.         {
  951.             self->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
  952.             self->monsterinfo.stand (self);
  953.             return;
  954.         }
  955.  
  956.         M_MoveToGoal (self, dist);
  957.  
  958.         if (!FindTarget (self))
  959.             return;
  960.     }
  961.  
  962.     if (ai_checkattack (self, dist))
  963.         return;
  964.  
  965.     if (self->monsterinfo.attack_state == AS_SLIDING)
  966.     {
  967.         ai_run_slide (self, dist);
  968.         return;
  969.     }
  970.  
  971.     if (enemy_vis)
  972.     {
  973. //        if (self.aiflags & AI_LOST_SIGHT)
  974. //            dprint("regained sight\n");
  975.         M_MoveToGoal (self, dist);
  976.         self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
  977.         VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
  978.         self->monsterinfo.trail_time = level.time;
  979.         return;
  980.     }
  981.  
  982.     // coop will change to another enemy if visible
  983.     if (coop->value)
  984.     {    // FIXME: insane guys get mad with this, which causes crashes!
  985.         if (FindTarget (self))
  986.             return;
  987.     }
  988.  
  989.     if ((self->monsterinfo.search_time) && (level.time > (self->monsterinfo.search_time + 20)))
  990.     {
  991.         M_MoveToGoal (self, dist);
  992.         self->monsterinfo.search_time = 0;
  993. //        dprint("search timeout\n");
  994.         return;
  995.     }
  996.  
  997.     save = self->goalentity;
  998.     tempgoal = G_Spawn();
  999.     self->goalentity = tempgoal;
  1000.  
  1001.     new = false;
  1002.  
  1003.     if (!(self->monsterinfo.aiflags & AI_LOST_SIGHT))
  1004.     {
  1005.         // just lost sight of the player, decide where to go first
  1006. //        dprint("lost sight of player, last seen at "); dprint(vtos(self.last_sighting)); dprint("\n");
  1007.         self->monsterinfo.aiflags |= (AI_LOST_SIGHT | AI_PURSUIT_LAST_SEEN);
  1008.         self->monsterinfo.aiflags &= ~(AI_PURSUE_NEXT | AI_PURSUE_TEMP);
  1009.         new = true;
  1010.     }
  1011.  
  1012.     if (self->monsterinfo.aiflags & AI_PURSUE_NEXT)
  1013.     {
  1014.         self->monsterinfo.aiflags &= ~AI_PURSUE_NEXT;
  1015. //        dprint("reached current goal: "); dprint(vtos(self.origin)); dprint(" "); dprint(vtos(self.last_sighting)); dprint(" "); dprint(ftos(vlen(self.origin - self.last_sighting))); dprint("\n");
  1016.  
  1017.         // give ourself more time since we got this far
  1018.         self->monsterinfo.search_time = level.time + 5;
  1019.  
  1020.         if (self->monsterinfo.aiflags & AI_PURSUE_TEMP)
  1021.         {
  1022. //            dprint("was temp goal; retrying original\n");
  1023.             self->monsterinfo.aiflags &= ~AI_PURSUE_TEMP;
  1024.             marker = NULL;
  1025.             VectorCopy (self->monsterinfo.saved_goal, self->monsterinfo.last_sighting);
  1026.             new = true;
  1027.         }
  1028.         else if (self->monsterinfo.aiflags & AI_PURSUIT_LAST_SEEN)
  1029.         {
  1030.             self->monsterinfo.aiflags &= ~AI_PURSUIT_LAST_SEEN;
  1031.             marker = PlayerTrail_PickFirst (self);
  1032.         }
  1033.         else
  1034.         {
  1035.             marker = PlayerTrail_PickNext (self);
  1036.         }
  1037.  
  1038.         if (marker)
  1039.         {
  1040.             VectorCopy (marker->s.origin, self->monsterinfo.last_sighting);
  1041.             self->monsterinfo.trail_time = marker->timestamp;
  1042.             self->s.angles[YAW] = self->ideal_yaw = marker->s.angles[YAW];
  1043. //            dprint("heading is "); dprint(ftos(self.ideal_yaw)); dprint("\n");
  1044.  
  1045. //            debug_drawline(self.origin, self.last_sighting, 52);
  1046.             new = true;
  1047.         }
  1048.     }
  1049.  
  1050.     VectorSubtract (self->s.origin, self->monsterinfo.last_sighting, v);
  1051.     d1 = VectorLength(v);
  1052.     if (d1 <= dist)
  1053.     {
  1054.         self->monsterinfo.aiflags |= AI_PURSUE_NEXT;
  1055.         dist = d1;
  1056.     }
  1057.  
  1058.     VectorCopy (self->monsterinfo.last_sighting, self->goalentity->s.origin);
  1059.  
  1060.     if (new)
  1061.     {
  1062. //        gi.dprintf("checking for course correction\n");
  1063.  
  1064.         tr = gi.trace(self->s.origin, self->mins, self->maxs, self->monsterinfo.last_sighting, self, MASK_PLAYERSOLID);
  1065.         if (tr.fraction < 1)
  1066.         {
  1067.             VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
  1068.             d1 = VectorLength(v);
  1069.             center = tr.fraction;
  1070.             d2 = d1 * ((center+1)/2);
  1071.             self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
  1072.             AngleVectors(self->s.angles, v_forward, v_right, NULL);
  1073.  
  1074.             VectorSet(v, d2, -16, 0);
  1075.             G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target);
  1076.             tr = gi.trace(self->s.origin, self->mins, self->maxs, left_target, self, MASK_PLAYERSOLID);
  1077.             left = tr.fraction;
  1078.  
  1079.             VectorSet(v, d2, 16, 0);
  1080.             G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target);
  1081.             tr = gi.trace(self->s.origin, self->mins, self->maxs, right_target, self, MASK_PLAYERSOLID);
  1082.             right = tr.fraction;
  1083.  
  1084.             center = (d1*center)/d2;
  1085.             if (left >= center && left > right)
  1086.             {
  1087.                 if (left < 1)
  1088.                 {
  1089.                     VectorSet(v, d2 * left * 0.5, -16, 0);
  1090.                     G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target);
  1091. //                    gi.dprintf("incomplete path, go part way and adjust again\n");
  1092.                 }
  1093.                 VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal);
  1094.                 self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
  1095.                 VectorCopy (left_target, self->goalentity->s.origin);
  1096.                 VectorCopy (left_target, self->monsterinfo.last_sighting);
  1097.                 VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
  1098.                 self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
  1099. //                gi.dprintf("adjusted left\n");
  1100. //                debug_drawline(self.origin, self.last_sighting, 152);
  1101.             }
  1102.             else if (right >= center && right > left)
  1103.             {
  1104.                 if (right < 1)
  1105.                 {
  1106.                     VectorSet(v, d2 * right * 0.5, 16, 0);
  1107.                     G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target);
  1108. //                    gi.dprintf("incomplete path, go part way and adjust again\n");
  1109.                 }
  1110.                 VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal);
  1111.                 self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
  1112.                 VectorCopy (right_target, self->goalentity->s.origin);
  1113.                 VectorCopy (right_target, self->monsterinfo.last_sighting);
  1114.                 VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
  1115.                 self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
  1116. //                gi.dprintf("adjusted right\n");
  1117. //                debug_drawline(self.origin, self.last_sighting, 152);
  1118.             }
  1119.         }
  1120. //        else gi.dprintf("course was fine\n");
  1121.     }
  1122.  
  1123.     M_MoveToGoal (self, dist);
  1124.  
  1125.     G_FreeEdict(tempgoal);
  1126.  
  1127.     if (self)
  1128.         self->goalentity = save;
  1129. }
  1130.